package phoenix.core.base;

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Pattern;

import core.config.Config;
import org.testng.TestException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;

import phoenix.core.PhoenixManager;
import phoenix.core.data_models.TestUserDataModel;
import phoenix.core.data_models.extentions.TestDataModelExt;
import phoenix.core.helpers.extensions.DBHelperExt;
import core.ApplicationManager;
import core.helpers.WebDriverHelper;

/**
 * The base class for test classes.
 */
public abstract class TestSuiteBase
{
	// PhoenixManager instance
	protected static PhoenixManager phoenix;

	// Test suite data
	public static String TEST_SUITE_NAME;
	public static String TEST_SUITE_DATE;
	public static String TEST_SUITE_TIME;
	// Test sphase data
	public static String TEST_PHASE_NAME;
	public static String TEST_PHASE_TIME;

	public TestSuiteBase()
	{
		phoenix = new PhoenixManager();
		TEST_SUITE_NAME = getCallerName(getCallerPath(3));
		System.setProperty("testSuiteName", TEST_SUITE_NAME);
	}

	/**
	 * Actions before all tests
	 * @param p_project        Test project name
	 * @param p_server         Test serbet type
	 * @param p_os             Test OS name
	 * @param p_platform       Test platform name
	 * @param p_site           Test site name
	 * @param p_build          Test build
	 * @param p_location       Test location name
	 * @param p_proxy          Test proxy name
	 * @param p_browserName    Test browser name
	 * @param p_browserVersion Test browser version
	 * @param p_saucelabsUser  SauceLabs username
	 * @param p_saucelabsKey   SauceLabs password
	 */
	@BeforeTest
	@Parameters(
		{ "project", "server", "os", "platform", "site", "build", "location", "proxy", "browserName", "browserVersion", "saucelabsUser", "saucelabsKey",
			"appiumPort", "appiumUdid", "appiumBootstrapPort", "appiumChromedriverPort" })
	public void testSuiteInit(String p_project, String p_server, String p_os, String p_platform, String p_site, String p_build, String p_location,
		String p_proxy, String p_browserName, String p_browserVersion, String p_saucelabsUser, String p_saucelabsKey, String p_appiumPort,
		String p_appiumUdid, String p_appiumBootstrapPort, String p_appiumChromedriverPort)
	{
		try
		{
			if (phoenix == null)
			{
				phoenix = new PhoenixManager();
			}
			// Set test suite date start
			TEST_SUITE_DATE = getCurrentDate("yyyy-MM-dd");
			System.setProperty("testSuiteDate", TEST_SUITE_DATE);
			// Set test suite time start
			TEST_SUITE_TIME = getCurrentDate("HH-mm-ss");
			System.setProperty("testSuiteTime", TEST_SUITE_TIME);
			// Set test phase name
			TEST_PHASE_NAME = getMethodName();

			// Set test phase start time
			// User Build
			Config.BUILD = p_build;
			// Set Build
			testUser().setBuild(Config.BUILD);

			TEST_PHASE_TIME = phoenix.cronos().getCurrentTime("HH-mm-ss");
			// Set test screenshot path
			ApplicationManager.TEST_SCREENSHOT_FILENAME = TEST_PHASE_NAME + "_" + TEST_SUITE_DATE + "_" + TEST_PHASE_TIME + ".png";
			ApplicationManager.TEST_SCREENSHOT_PATH = System.getProperty("user.dir")
				+ "\\reports"
				+ "\\" + TEST_SUITE_NAME
				+ "\\" + TEST_SUITE_DATE
				+ "\\" + TEST_SUITE_TIME
				+ "\\" + ApplicationManager.TEST_SCREENSHOT_FILENAME;
			;

			printTestStart("START TEST SUITE [" + TEST_SUITE_NAME + "]");

			try
			{
				phoenix.db().connect(
					phoenix.config().DB_HOST,
					phoenix.config().DB_PORT,
					phoenix.config().DB_NAME,
					phoenix.config().DB_USER,
					phoenix.config().DB_PASSWORD);

				db().isConnected();

			}
			catch (Exception p_ex)
			{
				throw new TestException(phoenix.log().getErrorsAndWarnings() + " " + p_ex.toString());
			}

			// Check Phoenix initializtion
			try
			{
				phoenix.initPhoenix(p_project, p_server, p_os, p_platform, p_site, p_build, p_location, p_proxy, p_browserName, p_browserVersion,
					p_saucelabsUser, p_saucelabsKey, p_appiumPort, p_appiumUdid, p_appiumBootstrapPort, p_appiumChromedriverPort);

				// A little trick to restart browser on init problems
				if (wd().isBrowserOpen() != true)
				{
					phoenix.log().warn("Browser is not found, going to restart it");
					// Do another try
					phoenix.initPhoenix(p_project, p_server, p_os, p_platform, p_site, p_build, p_location, p_proxy, p_browserName, p_browserVersion,
						p_saucelabsUser, p_saucelabsKey, p_appiumPort, p_appiumUdid, p_appiumBootstrapPort, p_appiumChromedriverPort);

					if (wd().isBrowserOpen() != true)
					{
						throw new TestException("Browser is not found");
					}
				}
			}
			catch (Exception p_ex)
			{
				throw new TestException(phoenix.log().getErrorsAndWarnings() + " " + p_ex.toString());
			}

			if (phoenix.testModel().getPlatform().equals(phoenix.config().PLATFORM_WEB))
			{

				if (!phoenix.testModel().getProxy().equals("def"))
				{
					phoenix.wd().openURL(phoenix.testModel().getSiteURL() + "/?ip_address=69.33.33.33");
					phoenix.wd().doBasicAuthentification();

				}
				else
				{
					phoenix.wd().openURL(phoenix.testModel().getSiteURL() + "/?ip_address=69.33.33.33");
					phoenix.setCountryCookie(phoenix.testModel().getLocation());
					phoenix.wd().openURL(phoenix.testModel().getSiteURL());
				}
			}
			else if (phoenix.testModel().getPlatform().equals(phoenix.config().PLATFORM_MOBILE))
			{
				phoenix.wd().openURL(phoenix.testModel().getSiteURL());
				phoenix.setCountryCookie(phoenix.testModel().getLocation());
			}

			phoenix.testModel().setScreenHight(phoenix.wd().driver().manage().window().getSize().getHeight());
			phoenix.testModel().setScreenWight(phoenix.wd().driver().manage().window().getSize().getWidth());
		}
		catch (Exception p_e)
		{
			phoenix.log().warn("testSuiteInit : " + p_e.getMessage());
		}
	}

	/**
	 * Actions before every test method
	 */
	@BeforeMethod
	public void testInit(Method p_method)
	{

		if (phoenix.config().REDMINE_POST_ERRORS)
		{
			phoenix.redmine().clearAttachments(); // Clear attachments
			phoenix.testModel().setIssueId(""); // Clear redmine ID
		}

		// Set test phase name
		TEST_PHASE_NAME = p_method.getName();
		System.setProperty("phaseName", TEST_PHASE_NAME);
		// Set test phase start time
		TEST_PHASE_TIME = phoenix.cronos().getCurrentTime("HH-mm-ss");
		System.setProperty("testTime", TEST_PHASE_TIME);
		// Set path to screenshots
		ApplicationManager.TEST_SCREENSHOT_FILENAME = TEST_PHASE_NAME + "_" + TEST_SUITE_DATE + "_" + TEST_PHASE_TIME + ".png";
		ApplicationManager.TEST_SCREENSHOT_PATH = System.getProperty("user.dir")
			+ "\\reports"
			+ "\\" + TEST_SUITE_NAME
			+ "\\" + TEST_SUITE_DATE
			+ "\\" + TEST_SUITE_TIME
			+ "\\" + ApplicationManager.TEST_SCREENSHOT_FILENAME;

		printTestStart("START TEST [" + TEST_PHASE_NAME + "]");
		phoenix.log().info("TestUserDataModel : " + testUser().getId());

		phoenix.testModel().setTestName(p_method.getName());
		phoenix.testModel().setStatus(2);
		phoenix.testModel().setDateStart(phoenix.cronos().getCurrentDate("yyyy-MM-dd HH:mm:ss"));
		phoenix.testModel().setTimeStart(phoenix.cronos().getCurrentTimeInMilliseconds());
		phoenix.testModel().setTimeRun(0);

		db().addTestData(phoenix.testModel());
	}

	/**
	 * Actions after every test method
	 */
	@AfterMethod
	public void testQuit()
	{
		printTestEnd("END TEST [" + TEST_PHASE_NAME + "]");
		phoenix.testModel().setTimeEnd(phoenix.cronos().getCurrentTimeInMilliseconds());
		phoenix.testModel().setDateEnd(phoenix.cronos().getCurrentDate("yyyy-MM-dd HH:mm:ss"));
		phoenix.testModel().setTimeRun(phoenix.testModel().getTimeEnd() - phoenix.testModel().getTimeStart());
		db().updateTestData(phoenix.testModel());
		phoenix.log().clear();
	}

	/**
	 * Actions after all tests
	 */
	@AfterTest
	public void testSuiteQuit()
	{
		printTestEnd("END TEST SUITE [" + TEST_SUITE_NAME + "]");
		phoenix.screenLogger().stop();
		phoenix.db().disconnect();
		phoenix.wd().stop();
		phoenix.shutDown();
		phoenix = null;
	}

	/**
	 * Link method to WebDriverHelper
	 * @return WebDriverHelper instance
	 */
	public WebDriverHelper wd()
	{
		return phoenix.wd();
	}

	/**
	 * Link method to DBHelperExt
	 * @return DBHelperExt instance
	 */
	public DBHelperExt db()
	{
		return phoenix.db();
	}

	/**
	 * Link method to TestDataModel
	 * @return TestDataModel instance
	 */
	public TestUserDataModel testUser()
	{
		return phoenix.testUser();
	}

	/**
	 * Gets current method name
	 * @return Method name
	 */
	private String getMethodName()
	{
		StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
		StackTraceElement stackTraceElement = stacktrace[2];// coz 0th will be getStackTrace so 1st
		String methodName = stackTraceElement.getMethodName();
		return methodName;
	}

	/**
	 * Prints test start info
	 * @param p_message Text message
	 */
	private void printTestStart(String p_message)
	{
		System.out.println("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> " + p_message + " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
		phoenix.log().info(p_message);
	}

	/**
	 * Prints test end info
	 * @param p_message Text message
	 */
	private void printTestEnd(String p_message)
	{
		phoenix.log().info(p_message);
		System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< " + p_message + " <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
	}

	/**
	 * Gets current date
	 * @param p_format Date format
	 * @return Date
	 */
	private String getCurrentDate(String p_format)
	{
		Date date = new Date();
		DateFormat dateFormat = new SimpleDateFormat(p_format);
		dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+3"));
		String currentDate = dateFormat.format(date);
		return currentDate;
	}

	/**
	 * Initializes ApplicationManager
	 */
	@BeforeSuite
	public void init()
	{
		testUser().clean();
	}

	/**
	 * After suite actions
	 */
	@AfterSuite
	public void shutdown()
	{
		if (phoenix != null)
		{
			phoenix.screenLogger().stop();
			phoenix.wd().stop();
			phoenix.db().disconnect();
		}
	}

	/**
	 * Gets the caller name
	 * @param p_callerPath
	 * @return Name of caller class
	 */
	public String getCallerName(String p_callerPath)
	{
		String[] callerPathSplit = p_callerPath.split(Pattern.quote("."));
		String callerName = callerPathSplit[callerPathSplit.length - 1];

		return callerName;
	}

	/**
	 * Gets the caller name
	 * @return Name of caller class
	 */
	public String getCallerPath(int p_depth)
	{
		String callerPath = Thread.currentThread().getStackTrace()[p_depth].getClassName();

		return callerPath;
	}

	/**
	 * Link method to TestDataModelExt
	 * @return TestDataModelExt instance
	 */
	public TestDataModelExt testModel()
	{
		if (phoenix == null)
		{
			// Initialize the ApplicationManager
			phoenix = new PhoenixManager();
		}

		return phoenix.testModel();
	}
}
